#include "number.h"

mpz_t a_tmp,
      b_tmp,
      tmp_num_pad,
      tmp_num_round,
      tmp_real_new_rounded,
      tmp_real_from_str,
      tmp_real_from_mpfr,
      tmp_real_zero,
      tmp_real_one,
      tmp_real_pi,
      tmp_real_e,
      tmp_real_abs,
      tmp_real_negate,
      tmp_real_ceil,
      tmp_real_floor,
      tmp_real_round,
      tmp_real_round_infinity,
      tmp_real_round_origin,
      tmp_real_add,
      tmp_real_subtract,
      tmp_real_multiply,
      tmp_real_divide;

/*
 *  Initializes mpz_t variables for use in Real functions.
 */
void init_real_tmp_nums ()
{
    mpz_init(a_tmp);
    mpz_init(b_tmp);
    mpz_init(tmp_num_pad);
    mpz_init(tmp_num_round);
    mpz_init(tmp_real_new_rounded);
    mpz_init(tmp_real_from_str);
    mpz_init(tmp_real_from_mpfr);
    mpz_init(tmp_real_zero);
    mpz_init(tmp_real_one);
    mpz_init(tmp_real_pi);
    mpz_init(tmp_real_e);
    mpz_init(tmp_real_abs);
    mpz_init(tmp_real_negate);
    mpz_init(tmp_real_ceil);
    mpz_init(tmp_real_floor);
    mpz_init(tmp_real_round);
    mpz_init(tmp_real_round_infinity);
    mpz_init(tmp_real_round_origin);
    mpz_init(tmp_real_add);
    mpz_init(tmp_real_subtract);
    mpz_init(tmp_real_multiply);
    mpz_init(tmp_real_divide);
}

/*
 *  Pads num with n additional zeros.
 *  
 *    num: (1234), n: 2 => num: (123400)
 */
void num_pad (mpz_t num, int n)
{
    mpz_ui_pow_ui(tmp_num_pad, 10, n);
    mpz_mul(num, num, tmp_num_pad);
}

/*
 *  Truncates num by n digits, rounding the rightmost remaining digit
 *  using round_mode.
 *  
 *      num: (12345), n: 3, round_mode: ROUND_UP => num: (13)
 */
void num_round (mpz_t num, int n, int round_mode)
{
    if (n <= 0)
    {
        return;
    }
    
    mpz_ui_pow_ui(tmp_num_round, 10, n);
    
    switch (round_mode)
    {
        case ROUND_DOWN:
            mpz_fdiv_q(num, num, tmp_num_round);
            break;
        
        case ROUND_UP:
            mpz_cdiv_q(num, num, tmp_num_round);
            break;
        
        case ROUND_NEAREST:
        // fix this
            //mpz_ui_pow_ui(tmp_num_round, 10, n);
            mpz_fdiv_q(num, num, tmp_num_round);
            //mpz_sub_ui(num, num, 5);
            //mpz_fdiv_q_ui(num, num, 10);
            break;
        
        case ROUND_UP1:
            mpz_fdiv_q(num, num, tmp_num_round);
            mpz_add_ui(num, num, 1);
            break;
    }
}

/*
 *  Returns the number of digits in num.
 *  
 *    num: (5600) => long: 4
 *    num: (-123) => long: 3
 */
long num_len (mpz_t num)
{
    int base = 10;
    int sign = (mpz_sgn(num) == -1);
    char buf[mpz_sizeinbase(num, base) + sign + 1];
    
    mpz_get_str(buf, base, num);
    return strlen(buf) - sign;
}

/*
 *  Frees the memory block pointed to by real.
 */
void real_free (Real* real)
{
    if (real)
    {
        if (REAL(real) && real->num)
        {
            mpz_clear(real->num);
        }
        
        free(real);
        real = NULL;
    }
}

/*
 *  Returns the size of the string representation of real as a long.
 */
long real_strlen (Real* real)
{
    switch (real->flag)
    {
        case NAN_FLAG:
            return 2;
        
        case POS_INF_FLAG:
        case NEG_INF_FLAG:
            return 4;
        
        case REAL_FLAG:;
            char buf[100];
            sprintf(buf, "%ld", real->exp);
            
            int sign = (mpz_sgn(real->num) == -1);
            int digits = num_len(real->num);
            int colon = 1;
            int exp = strlen(buf);
            
            return sign + digits + colon + exp;
    }
}

/*
 *  Puts a string representation of real into the string pointed to by buf.
 *  
 *    real: (561:1) => str: "561:1"
 *    real: (-3:-2) => str: "-3:-2"
 *    real: (+∞)    => str: "+∞"
 *    real: (ø)     => str: "ø"
 */
void real_print (char* buf, Real* real)
{
    switch (real->flag)
    {
        case NAN_FLAG:
            sprintf(buf, "ø");
            break;
        
        case POS_INF_FLAG:
            sprintf(buf, "+∞");
            break;
        
        case NEG_INF_FLAG:
            sprintf(buf, "-∞");
            break;
        
        case REAL_FLAG:;
            int base = 10;
            int sign = 1;
            char num_buf[num_len(real->num) + sign + 1];
            
            mpz_get_str(num_buf, base, real->num);
            sprintf(buf, "%s:%ld", num_buf, real->exp);
            break;
    }
}

/*
 *  Sets a_tmp and b_tmp to values equal to a->num and b->num. If necessary,
 *  a_tmp or b_tmp will be padded with zeros so that the units of their
 *  last-place digits are the same. The max exponent and max length of a->num
 *  and b->num will be stored in the pointers exp and len.
 */
void real_pad_tmps (Real* a, Real* b, long* exp, long* len)
{
    mpz_set(a_tmp, a->num);
    mpz_set(b_tmp, b->num);
    
    long a_len = num_len(a_tmp);
    long b_len = num_len(b_tmp);
    
    long a_last_place = 1 + a->exp - a_len;
    long b_last_place = 1 + b->exp - b_len;
    
    long pad = a_last_place - b_last_place;
    
    if (pad > 0)
    {
        a_len += pad;
        num_pad(a_tmp, pad);
    }
    else if (pad < 0)
    {
        b_len += -pad;
        num_pad(b_tmp, -pad);
    }
    
    if (exp != NULL)
    {
        *exp = max(a->exp, b->exp);
    }
    
    if (len != NULL)
    {
        *len = max(a_len, b_len);
    }
}

/*
 *  Returns 1 if real equals zero, 0 otherwise.
 */
int real_zero_p (Real* real)
{
    return ZERO(real);
}

/*
 *  Returns 1 if real is negative, 0 otherwise.
 */
int real_negative_p (Real* real)
{
    return NEG(real);
}

/*
 *  Returns 1 if real is positive, 0 otherwise.
 */
int real_positive_p (Real* real)
{
    return POS(real);
}

/*
 *  Returns 1 if a > b, 0 if a == b, or -1 if a < b. Returns 0 if a or b is NaN.
 */
int real_cmp (Real* a, Real* b)
{
    if (!REAL(a) || !REAL(b))
    {
        return (NaN(a) || NaN(b)) ? 0 : POS_INF(a) ? (POS_INF(b) ? 0 : 1) : POS_INF(b) ? -1 : NEG_INF(a) ? (NEG_INF(b) ? 0 : -1) : 1;
    }
    
    int a_sign = mpz_sgn(a->num);
    int b_sign = mpz_sgn(b->num);
    
    if (a_sign != b_sign)
    {
        return (a_sign > b_sign) ? 1 : -1;
    }
    else if (a->exp != b->exp)
    {
        return (a_sign == -1) ? ((a->exp < b->exp) ? 1 : -1) : ((a->exp > b->exp) ? 1 : -1);
    }
    
    real_pad_tmps(a, b, NULL, NULL);
    
    return mpz_cmp(a_tmp, b_tmp);
}

/*
 *  Returns 1 if a == b, 0 otherwise. (Returns 1 if a or b is NaN.)
 */
int real_eq_p (Real* a, Real* b)
{
    return real_cmp(a, b) == 0;
}

/*
 *  Returns 1 if a > b, 0 otherwise.
 */
int real_gt_p (Real* a, Real* b)
{
    return real_cmp(a, b) == 1;
}

/*
 *  Returns 1 if a >= b, 0 otherwise. (Returns 1 if a or b is NaN.)
 */
int real_ge_p (Real* a, Real* b)
{
    return real_cmp(a, b) != -1;
}

/*
 *  Returns 1 if a < b, 0 otherwise.
 */
int real_lt_p (Real* a, Real* b)
{
    return real_cmp(a, b) == -1;
}

/*
 *  Returns 1 if a <= b, 0 otherwise. (Returns 1 if a or b is NaN.)
 */
int real_le_p (Real* a, Real* b)
{
    return real_cmp(a, b) != 1;
}

/*
 *  Returns a pointer to a newly allocated Real.
 */
Real* real_alloc ()
{
    Real* real = malloc(sizeof(Real));
    
    if (real == NULL)
    {
        return NULL;
    }
    
    memset(real, 0, sizeof(Real));
    
    return real;
}

/*
 *  Returns a pointer to a newly allocated Real representing NaN, +∞, or -∞.
 *  
 *    flag: NAN_FLAG => real: (ø)
 *    flag: POS_INF_FLAG => real: (+∞)
 *    flag: NEG_INF_FLAG => real: (-∞)
 */
Real* real_new_flagged (int flag)
{
    Real* real = real_alloc();
    
    real->exp = 0;
    real->flag = flag;
    
    return real;
}

/*
 *  Returns a pointer to a newly allocated Real with the given value. Removes
 *  any trailing zero digits.
 *  
 *    num: (123), exp: 3 => real: (123:3)
 *    num: (-234), exp: 5 => real: (-234:5)
 *    num: (1000), exp: 4 => real: (1:4)
 */
Real* real_new_exact (mpz_t num, long exp)
{
    Real* real = real_alloc();
    
    real->exp = exp;
    real->flag = REAL_FLAG;
    
    mpz_init(real->num);
    
    if (real->num == NULL)
    {
        free(real);
        return NULL;
    }
    
    mpz_set(real->num, num);
    
    if (mpz_sgn(real->num) == 0)
    {
        real->exp = 0;
    }
    else
    {
        while (mpz_divisible_ui_p(real->num, 10))
        {
            mpz_divexact_ui(real->num, real->num, 10);
        }
    }
    
    return real;
}

/*
 *  Returns a pointer to a newly allocated Real with the given value. If the num
 *  is longer than digs, rounds num using the given round_mode. Removes any
 *  trailing zero digits.
 *  
 *    num: (12), exp: 5 => real: (12:5)
 *    num: (12), exp: -5 => real: (12:-5)
 *    num: (12300), exp: 2, digs: 10 => real: (123:2)
 *    num: (1231), exp: 2, digs: 2, round_mode: ROUND_UP => real: (13:2)
 */
Real* real_new_rounded (mpz_t num, long exp, int round_mode)
{
    long overrun = num_len(num) - digs;
    mpz_set(tmp_real_new_rounded, num);
    
    if (overrun > 0)
    {
        num_round(tmp_real_new_rounded, overrun, round_mode);
    }
    
    return real_new_exact(tmp_real_new_rounded, exp);
}

/*
 *  Returns a pointer to a newly allocated Real with the value represented by
 *  the given string and exp. The string must contain only an optional negative
 *  followed by one or more digits between 0 and 9.
 *  
 *    str: "3456", exp: 2 => real: (3456:2)
 *    str: "-234", exp: -1 => real: (-234:-1)
 */
Real* real_from_str (char* num_str, long exp)
{
    int base = 10;
    mpz_set_str(tmp_real_from_str, num_str, base);
    
    return real_new_exact(tmp_real_from_str, exp);
}

/*
 *  Returns a pointer to a newly allocated Real with the given value. If the num
 *  is longer than digs, rounds num using the given round_mode. Removes any
 *  trailing zero digits.
 */
Real* real_from_mpfr (mpfr_t num, int round_mode)
{
    if (!mpfr_number_p(num))
    {
        return real_new_flagged(mpfr_nan_p(num) ? NAN_FLAG : (mpfr_sgn(num) == 1) ? POS_INF_FLAG : NEG_INF_FLAG);
    }
    
    mpfr_exp_t exp;
    int base = 10;
    int sign = 1;
    int digits = digs + 5;
    char buf[digits + sign + 1];
    
    mpfr_get_str(buf, &exp, base, digits, num, MPFR_RNDN);
    mpz_set_str(tmp_real_from_mpfr, buf, base);
    
    return real_new_rounded(tmp_real_from_mpfr, (long)exp, round_mode);
}

/*
 *  Returns a pointer to a newly allocated Real representing a NaN value.
 */
Real* real_nan ()
{
    return real_new_flagged(NAN_FLAG);
}

/*
 *  Returns a pointer to a newly allocated Real representing +∞.
 */
Real* real_pos_inf ()
{
    return real_new_flagged(POS_INF_FLAG);
}

/*
 *  Returns a pointer to a newly allocated Real representing -∞.
 */
Real* real_neg_inf ()
{
    return real_new_flagged(NEG_INF_FLAG);
}

/*
 *  Returns a pointer to a newly allocated Real containing 0.
 */
Real* real_zero ()
{
    mpz_set_ui(tmp_real_zero, 0);
    return real_new_exact(tmp_real_zero, 0);
}

/*
 *  Returns a pointer to a newly allocated Real containing 1.
 */
Real* real_one ()
{
    mpz_set_ui(tmp_real_one, 1);
    return real_new_exact(tmp_real_one, 1);
}

/*
 *  Returns a pointer to a newly allocated Real containing digs digits of pi.
 *  The rightmost digit is rounded using round_mode.
 *  
 *    digs: 5, round_mode: ROUND_DOWN => real: (31415:1)
 *    digs: 3, round_mode: ROUND_UP1 => real: (315:1)
 */
Real* real_pi (int round_mode)
{
    long len = digs + 1;
    char buf[len];
    
    strncpy(buf, PI, len);
    buf[len] = '\0';
    
    mpz_set_str(tmp_real_pi, buf, 10);
    
    return real_new_rounded(tmp_real_pi, 1, round_mode);
}

/*
 *  Returns a pointer to a newly allocated Real containing digs digits of e.
 *  The rightmost digit is rounded using round_mode.
 *  
 *    digs: 5, round_mode: ROUND_NEAREST => real: (27183:1)
 *    digs: 3, round_mode: ROUND_UP1 => real: (272:1)
 */
Real* real_e (int round_mode)
{
    long len = digs + 1;
    char buf[len];
    
    strncpy(buf, E, len);
    buf[len] = '\0';
    
    mpz_set_str(tmp_real_e, buf, 10);
    
    return real_new_rounded(tmp_real_e, 1, round_mode);
}

/*
 *  Returns a pointer to a newly allocated Real with the same value as the given
 *  real.
 */
Real* real_dup (Real* real)
{
    return REAL(real) ? real_new_exact(real->num, real->exp) : real_new_flagged(real->flag);
}

/*
 *  Returns a pointer to a newly allocated Real containing the absolute value of
 *  real.
 *  
 *    real: (1:1) => real: (1:1)
 *    real: (-1:1) => real: (1:1)
 */
Real* real_abs (Real* real)
{
    if (!REAL(real))
    {
        return NaN(real) ? real_nan() : real_pos_inf();
    }
    
    mpz_abs(tmp_real_abs, real->num);
    return real_new_exact(tmp_real_abs, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing the negative of real.
 *  
 *    real: (1:1) => real: (-1:1)
 *    real: (-1:1) => real: (1:1)
 */
Real* real_negate (Real* real)
{
    if (!REAL(real))
    {
        return NaN(real) ? real_nan() : POS(real) ? real_neg_inf() : real_pos_inf();
    }
    
    mpz_neg(tmp_real_negate, real->num);
    return real_new_exact(tmp_real_negate, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing the smallest integer
 *  greater than or equal to real.
 *  
 *    real: (1:1) => real: (1:1)
 *    real: (5:0) => real: (1:1)
 *    real: (-5:0) => real: (0:1)
 */
Real* real_ceil (Real* real)
{
    REAL_CHECK(real);
    
    mpz_set(tmp_real_ceil, real->num);
    
    long len = num_len(tmp_real_ceil);
    long dec = len - real->exp;
    
    if (dec > 0)
    {
        num_round(tmp_real_ceil, dec, ROUND_UP);
    }
    
    return real_new_exact(tmp_real_ceil, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing the largest integer
 *  less than or equal to real.
 *  
 *    real: (1:1) => real: (1:1)
 *    real: (5:0) => real: (0:1)
 *    real: (-5:0) => real: (-1:1)
 */
Real* real_floor (Real* real)
{
    REAL_CHECK(real);
    
    mpz_set(tmp_real_floor, real->num);
    
    long len = num_len(tmp_real_floor);
    long dec = len - real->exp;
    
    if (dec > 0)
    {
        num_round(tmp_real_floor, dec, ROUND_DOWN);
    }
    
    return real_new_exact(tmp_real_floor, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing the nearest integer
 *  to real (half rounds toward positive infinity).
 *  
 *    real: (1:1) => real: (1:1)
 *    real: (5:0) => real: (1:1)
 *    real: (-5:0) => real: (0:1)
 */
Real* real_round (Real* real)
{
    REAL_CHECK(real);
    
    mpz_set(tmp_real_round, real->num);
    
    long len = num_len(tmp_real_round);
    long dec = len - real->exp;
    
    if (dec > 0)
    {
        num_round(tmp_real_round, dec, ROUND_NEAREST);
    }
    
    return real_new_exact(tmp_real_round, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing the nearest integer
 *  to real in the direction away from the origin.
 *  
 *    real: (1:1) => real: (1:1)
 *    real: (5:0) => real: (1:1)
 *    real: (-5:0) => real: (-1:1)
 */
Real* real_round_infinity (Real* real)
{
    REAL_CHECK(real);
    
    mpz_set(tmp_real_round_infinity, real->num);
    
    long len = num_len(tmp_real_round_infinity);
    long dec = len - real->exp;
    
    if (dec > 0)
    {
        num_round(tmp_real_round_infinity, dec, mpz_sgn(tmp_real_round_infinity) == 1 ? ROUND_UP : ROUND_DOWN);
    }
    
    return real_new_exact(tmp_real_round_infinity, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing the nearest integer
 *  to real in the direction of the origin.
 *  
 *    real: (1:1) => real: (1:1)
 *    real: (5:0) => real: (0:1)
 *    real: (-5:0) => real: (0:1)
 */
Real* real_round_origin (Real* real)
{
    REAL_CHECK(real);
    
    mpz_set(tmp_real_round_origin, real->num);
    
    long len = num_len(tmp_real_round_origin);
    long dec = len - real->exp;
    
    if (dec > 0)
    {
        num_round(tmp_real_round_origin, dec, mpz_sgn(tmp_real_round_origin) == 1 ? ROUND_DOWN : ROUND_UP);
    }
    
    return real_new_exact(tmp_real_round_origin, real->exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing a + b. The result is
 *  not rounded.
 *  
 *    a: (1:1), b: (25:0) => real: (125:1)
 */
Real* real_add (Real* a, Real* b)
{
    long exp;
    long len;
    
    if (!REAL(a) || !REAL(b))
    {
        return (NaN(a) || NaN(b) || (POS_INF(a) && NEG_INF(b)) || (NEG_INF(a) && POS_INF(b))) ? real_nan() : (POS_INF(a) || POS_INF(b)) ? real_pos_inf() : real_neg_inf();
    }
    
    real_pad_tmps(a, b, &exp, &len);
    mpz_add(tmp_real_add, a_tmp, b_tmp);
    
    exp += (num_len(tmp_real_add) - len);
    
    return real_new_exact(tmp_real_add, exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing a - b. The result is
 *  not rounded.
 *  
 *    a: (2:1), b: (25:0) => real: (175:1)
 */
Real* real_subtract  (Real* a, Real* b)
{
    long exp;
    long len;
    
    if (!REAL(a) || !REAL(b))
    {
        return (NaN(a) || NaN(b) || (POS_INF(a) && POS_INF(b)) || (NEG_INF(a) && NEG_INF(b))) ? real_nan() : (POS_INF(a) || NEG_INF(b)) ? real_pos_inf() : real_neg_inf();
    }
    
    real_pad_tmps(a, b, &exp, &len);
    mpz_sub(tmp_real_subtract, a_tmp, b_tmp);
     
    exp += (num_len(tmp_real_subtract) - len);
    
    return real_new_exact(tmp_real_subtract, exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing a * b. The result is
 *  not rounded.
 *  
 *    a: (2:1), b: (164:1) => real: (328:1)
 */
Real* real_multiply (Real* a, Real* b)
{
    if (!REAL(a) || !REAL(b))
    {
        return (NaN(a) || NaN(b) || ZERO(a) || ZERO(b)) ? real_nan() : ((POS(a) && POS(b)) || (NEG(a) && NEG(b))) ? real_pos_inf() : real_neg_inf();
    }
    
    long a_len = num_len(a->num);
    long b_len = num_len(b->num);
    
    mpz_mul(tmp_real_multiply, a->num, b->num);
    long exp = num_len(tmp_real_multiply) - ((a_len - a->exp) + (b_len - b->exp));
    
    return real_new_exact(tmp_real_multiply, exp);
}

/*
 *  Returns a pointer to a newly allocated Real containing a ÷ b. If the number
 *  of digits in the result is greater than digs, rounds using round_mode.
 *
// EXAMPLES ARE WRONG
 *    a: (2:1), b: (164:1), prec: 3 => real: (328:1)
 *    a: (2:1), b: (164:1), prec: 1, round_mode: ROUND_NEAREST => real: (33:1)
 */
Real* real_divide (Real* a, Real* b, int round_mode)
{
    mpfr_t a_num;
    mpfr_t b_num;
    mpfr_t num;
    
    if (!REAL(a) || !REAL(b))
    {
        return (NaN(a) || NaN(b) || (!REAL(a) && !REAL(b))) ? real_nan() : !REAL(b) ? real_zero() : (POS(a) && POS(b)) ? real_pos_inf() : real_neg_inf();
    }
    
    mpfr_init2(a_num, PREC);
    mpfr_init2(b_num, PREC);
    mpfr_set_r(a_num, a);
    mpfr_set_r(b_num, b);
    
    mpfr_init2(num, PREC);
    mpfr_div(num, a_num, b_num, MPFR_RNDN);
    
    Real* result = real_from_mpfr(num, round_mode);
    
    mpfr_clear(a_num);
    mpfr_clear(b_num);
    mpfr_clear(num);
    
    return result;
}

//{
//    int dec;
//    mpz_t num;
//    
//    mpz_init(num);
//    
//    if (mpz_divisible_p(a->num, b->num))
//    {
//        mpz_divexact(num, a->num, b->num);
//        dec = a->dec - b->dec;
//    }
//    else
//    {
//        real_pad_ab_tmp(a, b);
//        num_pad(a_tmp, prec);
//        
//        if (mpz_divisible_p(a_tmp, b_tmp))
//        {
//            mpz_divexact(num, a_tmp, b_tmp);
//            dec = prec;
//        }
//        else
//        {
//            num_pad(a_tmp, 1);
//            mpz_fdiv_q(num, a_tmp, b_tmp);
//            
//            round_mode = (round_mode == ROUND_UP) ? ROUND_UP1 : round_mode;
//            dec = prec + 1;
//        }
//    }
//    
//    return real_new(num, num_len(num) - dec, prec, round_mode);
//}

/*
 *  Returns a pointer to a newly allocated Real containing the modulo of a and
 *  b. If the number of decimal places in the result is greater than prec,
 *  rounds using round_mode.
 *  
 *    a: (2:1), b: (164:1), prec: 3 => real: (328:1)
 *    a: (2:1), b: (164:1), prec: 1, round_mode: ROUND_NEAREST => real: (33:1)
 */
//Real* real_modulo (Real* a, Real* b, unsigned int prec, int round_mode)
//{
//    int dec;
//    int sgn;
//    mpz_t num;
//    
//    dec = real_pad_ab_tmp(a, b);
//    
//    mpz_init(num);
//    
//    if (mpz_divisible_p(a_tmp, b_tmp))
//    {
//        return real_new0(num, 0, 1, 1, 0);
//    }
//    
//    mpz_mod(num, a_tmp, b_tmp);
//    
//    if (b->neg)
//    {
//        mpz_neg(num, num);
//    }
//    
//    return real_new(num, num_len(num) - dec, prec, round_mode);
//}
